//=============================================================================
/**
* @file kprocessutil.cpp
* @brief 
* @author qiuruifeng <qiuruifeng@kingsoft.com>
* @date 2009-9-1   16:22
*/
//=============================================================================
#include "stdafx.h"
#include <Windows.h>
#include <Userenv.h>
#include <string.h>
#include "kprocessutil.h"

#pragma comment(lib, "Userenv.lib")

int KWinExecW(const wchar_t* pszCmdLine,
				const wchar_t* pszCurDir, 
				DWORD dwCmdShow, 
				DWORD dwWaitTime, 
				DWORD* pdwGetExitCodeErr,
				DWORD* pdwExitCode)
{
	if (NULL == pszCmdLine)
		return -1;

	if (!((pdwGetExitCodeErr && pdwExitCode) || (!pdwGetExitCodeErr && !pdwExitCode)))
		return -1;

	DWORD				dwRetCode	= 0;
	DWORD				dwCreateFlag= 0;
	PROCESS_INFORMATION ProcessInfo = { 0 };
	STARTUPINFO			StartupInfo = {0};
	StartupInfo.cb			= sizeof(STARTUPINFO);
	StartupInfo.dwFlags		= STARTF_USESHOWWINDOW;
	StartupInfo.wShowWindow = static_cast<WORD>(dwCmdShow);

	wchar_t* szExecCmd = NULL;
	size_t size = wcslen(pszCmdLine);
	szExecCmd = new wchar_t[size + 1];
	wcsncpy(szExecCmd, pszCmdLine, size + 1);

	if (SW_HIDE == dwCmdShow)
		dwCreateFlag = CREATE_NO_WINDOW;

	BOOL bRetCode = FALSE;
	bRetCode = ::CreateProcessW(
		NULL,
		szExecCmd,
		NULL,
		NULL,
		FALSE,
		dwCreateFlag,
		NULL,
		pszCurDir,
		&StartupInfo,
		&ProcessInfo
		);

	if (!bRetCode)
	{
		dwRetCode = ::GetLastError();		
	}
	else
	{
		dwRetCode = 0;
		if (dwWaitTime)
			::WaitForSingleObject(ProcessInfo.hProcess, dwWaitTime);

		if (pdwGetExitCodeErr && pdwExitCode)
		{
			if (::GetExitCodeProcess(ProcessInfo.hProcess, pdwExitCode))
				*pdwGetExitCodeErr = 0;
			else
				*pdwGetExitCodeErr = ::GetLastError();
		}

		::CloseHandle(ProcessInfo.hProcess);
		::CloseHandle(ProcessInfo.hThread);
	}

	if (szExecCmd)
	{
		delete[] szExecCmd;
		szExecCmd = NULL;
	}

	return dwRetCode;
}

int KWinExecAsUserW(const wchar_t* pszCmdLine,
                    const wchar_t* pszCurDir, 
                    DWORD dwCmdShow, 
                    DWORD dwWaitTime, 
                    HANDLE hToken,
                    DWORD* pdwGetExitCodeErr,
                    DWORD* pdwExitCode)
{
    if ( (NULL == pszCmdLine) || (NULL == hToken) )
        return -1;

    if (!((pdwGetExitCodeErr && pdwExitCode) || (!pdwGetExitCodeErr && !pdwExitCode)))
        return -1;

    DWORD				dwRetCode	= 0;
    DWORD				dwCreateFlag= 0;
    PROCESS_INFORMATION ProcessInfo = { 0 };
    STARTUPINFO			StartupInfo = {0};
    StartupInfo.cb			= sizeof(STARTUPINFO);
    StartupInfo.dwFlags		= STARTF_USESHOWWINDOW;
    StartupInfo.wShowWindow = static_cast<WORD>(dwCmdShow);

    wchar_t* szExecCmd = NULL;
    size_t size = wcslen(pszCmdLine);
    szExecCmd = new wchar_t[size + 1];
    wcsncpy(szExecCmd, pszCmdLine, size + 1);

    if (SW_HIDE == dwCmdShow)
        dwCreateFlag = CREATE_NO_WINDOW;

	void* pEnvironment = NULL;
	if ( !CreateEnvironmentBlock(
		&pEnvironment,
		hToken,
		FALSE
		))
	{
		return GetLastError();
	}
	
	if (pEnvironment)
		dwCreateFlag |= CREATE_UNICODE_ENVIRONMENT;

    BOOL bRetCode = FALSE;
    bRetCode = ::CreateProcessAsUserW(
        hToken,
        NULL,
        szExecCmd,
        NULL,
        NULL,
        FALSE,
        dwCreateFlag,
        pEnvironment,
        pszCurDir,
        &StartupInfo,
        &ProcessInfo
        );

    if (!bRetCode)
    {
        dwRetCode = ::GetLastError();		
    }
    else
    {
        dwRetCode = 0;
        if (dwWaitTime)
            ::WaitForSingleObject(ProcessInfo.hProcess, dwWaitTime);

        if (pdwGetExitCodeErr && pdwExitCode)
        {
            if (::GetExitCodeProcess(ProcessInfo.hProcess, pdwExitCode))
                *pdwGetExitCodeErr = 0;
            else
                *pdwGetExitCodeErr = ::GetLastError();
        }

        ::CloseHandle(ProcessInfo.hProcess);
        ::CloseHandle(ProcessInfo.hThread);
    }

    if (szExecCmd)
    {
        delete[] szExecCmd;
        szExecCmd = NULL;
    }

	if (pEnvironment)
	{
		DestroyEnvironmentBlock(pEnvironment);
	}
    return dwRetCode;
}